<template>
  <transition name="zoom">
    <div v-if="isVisible" class="view-container">
      <span class="preview-btn close-btn" @click.stop="handleClose">
        <el-icon><ele-CloseBold /></el-icon>
      </span>

      <div class="img-container">
        <img class="image-view" :style="style" :src="imageViewSrc" @mousedown.stop="handleDrag"
          @mousewheel="handleWheel" />
      </div>

      <div class="preview-btn preview-actions" @click.stop>
        <el-icon><ele-Plus @click="handleAction('zoomIn')" /></el-icon>
        <el-icon><ele-Minus @click="handleAction('zoomOut')" /></el-icon>
        <el-icon><ele-RefreshLeft @click="handleAction('anticlockwise')" /></el-icon>
        <el-icon><ele-RefreshRight @click="handleAction('clockwise')" /></el-icon>
        <!-- <el-icon><ele-Delete @click="deletePic" v-show="showDelete"/></el-icon> -->
      </div>

      <span class="preview-btn left-btn" @click.stop="prevEvent">
        <el-icon><ele-ArrowLeftBold /></el-icon>
      </span>

      <span class="preview-btn right-btn" @click.stop="nextEvent">
        <el-icon><ele-ArrowRightBold /></el-icon>
      </span>
    </div>
  </transition>
</template>

<script lang="ts" setup>
import { isNull } from '/@/utils';
const emit = defineEmits<{
  (e: 'deletePic', id: unknown): void
}>()
const props = defineProps({
  // 父组件传递 图片节点数组对象，包含每个图片的节点数据
  imageElements: {
    type: Array,
    default: () => [],
  },
  showDelete: {
    type: Boolean,
    default: false
  }
})
// 预览功能区
const imageHandle = ref({
  id: undefined as unknown,
  currentImageIndex: 0, // 当前图片索引
  src: '', //图片地址
  scale: 1, // 缩放
  deg: 0, // 旋转
  offsetX: 0, // 拖动
  offsetY: 0, // 拖动
  enableTransition: true, // 是否过渡
})
const currentImageEls = ref<any[]>([]); // 当前预览的图片对应的节点数组对象
const isVisible = ref<Boolean>(false); // 控制预览显示和隐藏
const dimension = ref<any>({}); // 预览图片的样式数据对象
const scaleAction = ref<number>(0.2); // 功能区每次缩放的倍数
const degAction = ref<number>(90); // 功能区每次旋转的度数
const scaleWheel = ref<number>(0.05); // 滚轮缩放的倍数

watch(() => props.imageElements, (newVal: any[]) => {
  if (!isNull(newVal)) {
    currentImageEls.value = newVal;
  }
}, {
  deep: true,
  immediate: true
})
/**
 * 初始化预览功能区
 * @param {number} [currentImageIndex=0] 当前图片对应节点数组的索引， 默认为0
 * @param {String} [src=''] 当前图片的地址 默认为null
 * @param {number} [deg=0] 旋转角度 默认为0
 * @param {number} [offsetX=0] 拖拽的偏移量 默认为0
 * @param {number} [offsetY=0] 拖拽的偏移量 默认为0
 * @param {number} [scale=1] 缩放 默认为1
 * @param {boolean} [enableTransition=true] 是否过渡 默认为true
 * */
const initImageHandle = ({
  id = undefined,
  currentImageIndex = 0,
  src = '',
  scale = 1,
  deg = 0,
  offsetX = 0,
  offsetY = 0,
  enableTransition = true,
}) => {
  // 旋转超过180°时，回正到180°以内，再显示过渡动画
  imageHandle.value.enableTransition = false;
  let role = imageHandle.value.deg / 360;
  if (Math.abs(role) > 0.5) {
    imageHandle.value.deg -= (role > 0 ? Math.ceil(role) : Math.floor(role)) * 360;
  }
  // 更新图片状态
  setTimeout(() => {
    imageHandle.value = {
      id,
      currentImageIndex,
      src,
      scale,
      deg,
      offsetX,
      offsetY,
      enableTransition,
    }
  }, 50);
}

/**
 * 工具函数，返回处于上下限之间的值
 * @param {number} lowLimit 下限
 * @param {number} value 需要控制的值
 * @param {number} upLimit 上限
 *
 * @return {number} value > 上限 返回上限upLimit；value < 下限 返回下限lowLimit； lowLimit < value < upLimit 返回value本身
 * */
const customClamp = (lowLimit: number, value: number, upLimit: number) => {
  return Math.min(upLimit, Math.max(lowLimit, value));
}
/**
 * 获取当前显示图片的样式， 宽、高、位置
 * @param {{}} el 图片节点对象
 *
 * @return {{}} 显示图片的样式对象，宽、高、位置
 * */
const getDimension = (el: any) => {
  const naturalWidth = el.naturalWidth, naturalHeight = el.naturalHeight;
  let height = 0, width = 0;

  const radio = naturalWidth / naturalHeight; // 宽高比
  // 放大后的宽高
  if (naturalHeight > naturalWidth) {
    // 以高度为基准
    height = customClamp(window.innerHeight * 0.7, naturalHeight, window.innerHeight);
    width = height * radio;
  } else {
    // 以宽度为基准
    width = customClamp(window.innerWidth * 0.7, naturalWidth, window.innerWidth);
    height = width / radio;
  }
  return {
    size: { width, height },
    // 放大后图片的位置
    position: {
      top: (window.innerHeight - height) / 2,
      left: (window.innerWidth - width) / 2,
    },
  }
}
/**
 * 关闭预览
 * */
const handleClose = () => {
  isVisible.value = false;
  initImageHandle({});
}
/**
 * 显示预览
 *
 * @param {{}} e 需要显示的图片的节点对象
 * */
const handleView = (e: any) => {
  // 当显示图片对应的索引
  const currentImageIndex = currentImageEls.value.findIndex(el => el.src == e.target.src);
  imageHandle.value.currentImageIndex = currentImageIndex;
  // 当显示图片对应的src地址
  imageHandle.value.src = e.target.src;
  // 存入id，用于删除图片操作
  imageHandle.value.id = e.target.id;
  // 显示预览
  isVisible.value = true;
  // 获取图片样式
  dimension.value = getDimension(e.target);
}
/**
 * 切换图片功能 —— 切换上一张图片，无缝循环切换
 * */
const prevEvent = () => {
  // 上一张
  const currentImageIndex = (imageHandle.value.currentImageIndex + props.imageElements.length - 1) % props.imageElements.length;
  let nowEl = currentImageEls.value[currentImageIndex];
  if (isNull(nowEl)) return;

  dimension.value = getDimension(nowEl) as any;
  const src = nowEl.src;
  const id = nowEl.id;
  initImageHandle({ id, currentImageIndex, src });
}
/**
 * 切换图片功能 —— 切换下一张图片，无缝循环切换
 * */
const nextEvent = () => {
  // 下一张
  const currentImageIndex = (imageHandle.value.currentImageIndex + 1) % props.imageElements.length;
  let nowEl = currentImageEls.value[currentImageIndex];
  if (isNull(nowEl)) return;

  dimension.value = getDimension(nowEl);
  const src = nowEl.src;
  const id = nowEl.id;
  initImageHandle({ id, currentImageIndex, src });
}
/**
 * 功能区控制， 放大， 缩小，顺时针旋转， 逆时针旋转
 * @param {String} action 功能类型，包括（zoomIn放大， zoomOut缩小，clockwise顺时针旋转， anticlockwise逆时针旋转）
 * @param {{}} [options={}] 额外设置， 如有需要可拓展
 * */
const handleAction = (action: string, options = {}) => {
  const { zoomRate, rotateDeg, enableTransition } = {
    zoomRate: scaleAction.value, // zoomRate: 每次缩放倍数
    rotateDeg: degAction.value, // rotateDeg: 每次旋转角度大小
    enableTransition: true, // 默认过渡
    ...options, // 其他配置
  }

  switch (action) {
    case 'zoomOut':
      // 缩小
      if (imageHandle.value.scale > scaleAction.value) {
        // 限制最小缩放级别
        imageHandle.value.scale = parseFloat((imageHandle.value.scale - zoomRate).toFixed(3));
      }
      break;
    case 'zoomIn':
      // 放大
      imageHandle.value.scale = parseFloat((imageHandle.value.scale + zoomRate).toFixed(3));
      break;
    case 'clockwise':
      // 顺时针旋转
      imageHandle.value.deg += rotateDeg;
      break;
    case 'anticlockwise':
      // 逆时针旋转
      imageHandle.value.deg -= rotateDeg;
      break;
  }
  // 过渡
  imageHandle.value.enableTransition = enableTransition;
}
/**
 * 删除图片
 * */
const deletePic = () => {
  emit("deletePic", imageHandle.value.id);
}
/**
 * 拖拽功能
 * @param {{}} e 鼠标点击事件
 * */
const handleDrag = (e: any) => {
  // 拖拽
  // 点击位置
  const { offsetX, offsetY } = imageHandle.value;
  const [startX, startY] = [e.pageX, e.pageY];

  // 鼠标移动
  const onMouseMove = (moveEvent: any) => {
    // 位移 ,图片移动
    imageHandle.value.offsetX = offsetX + moveEvent.pageX - startX;
    imageHandle.value.offsetY = offsetY + moveEvent.pageY - startY;
    // 关闭过渡
    imageHandle.value.enableTransition = false;
  }
  // 拖拽结束
  const onMouseUp = () => {
    imageHandle.value.enableTransition = true;
    e.target.removeEventListener('mousemove', onMouseMove);
    e.target.removeEventListener('mouseup', onMouseUp);
  }
  // 绑定事件监听
  e.target.addEventListener('mousemove', onMouseMove);
  e.target.addEventListener('mouseup', onMouseUp);
  // 阻止默认行为
  e.preventDefault();
}
/**
 * 滚轮缩放
 * @param {{}} e 滚轮事件
 * */
const handleWheel = (e: any) => {
  const delta = e.wheelDelta ? e.wheelDelta : -e.detail;
  // 判断鼠标滚轮是向上滚动还是向下滚动
  if (delta > 0) {
    // 向上 放大
    handleAction('zoomIn', {
      zoomRate: scaleWheel,
      enableTransition: false,
    })
  } else {
    // 向下 缩小
    handleAction('zoomOut', {
      zoomRate: scaleWheel,
      enableTransition: false,
    })
  }
}
/**
 * 预览显示图片的src地址
 * @return {String | undefined} 如果有图片地址返回地址，否则返回undefined
 * */
const imageViewSrc = computed(() => {
  if (isNull(imageHandle.value.src)) {
    return undefined;
  }
  return imageHandle.value.src;
})
/**
 * 预览图片的样式
 * @return {{}} 图片样式对象
 * */
const style = computed(() => {
  if (isNull(dimension)) return null;
  // 功能区控制样式
  const { scale, deg, offsetX, offsetY, enableTransition } = imageHandle.value;
  // 图片节点样式
  const {
    size: { width, height },
    position: { top, left },
  } = dimension.value;

  return {
    width: `${width}px`,
    height: `${height}px`,
    top: `${top}px`,
    left: `${left}px`,
    transform: `translate3d(${offsetX}px, ${offsetY}px, 0) scale(${scale}) rotate(${deg}deg)`,
    transition: enableTransition ? 'transform 0.3s' : 'none',
  }
})

defineExpose({
  handleView
})
</script>
<style scoped>
.zoom-enter,
.zoom-leave-to {
  /* TODO:消失的时候回到原来的位置 */
  transform: scale(0);
}

.zoom-enter-active,
.zoom-leave-active {
  transition: transform 0.3s;
}

.view-container {
  z-index: 1000;
  height: 100vh;
  width: 100vw;
  position: fixed;
  left: 0;
  top: 0;
  background: rgba(0, 0, 0, 0.6);
  cursor: default;
  transition: background 0.3s;
}

.preview-btn {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50% !important;
  opacity: 0.8;
  box-sizing: border-box;
  user-select: none;
}

.close-btn {
  z-index: 1001;
  width: 40px;
  height: 40px;
  top: 40px;
  right: 40px;
  font-size: 40px;
  background-color: #606266;
  color: #fff;
}

.img-container {
  width: 100%;
  height: 100%;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
}

.img-container .image-view {
  background-color: #fff;
  position: absolute;
  user-select: none;
  transform-origin: center;
  /* height: 50%; */
}

.img-container .image-view:active {
  cursor: grab;
}

.preview-actions {
  width: 400px;
  height: 48px;
  left: 50%;
  transform: translateX(-50%);
  bottom: 30px;
  background-color: #606266;
  color: #fff;
  border-radius: 24px !important;
  display: flex;
  justify-content: space-evenly;
  align-items: center;
  font-size: 30px;
}

.preview-actions i {
  border-radius: 20%;
  background-color: #fff1;
  cursor: pointer;
  height: 100%;
  width: 48px;
  text-align: center;
  line-height: 48px;
}

.left-btn,
.right-btn {
  height: 44px;
  width: 44px;
  top: 50%;
  font-size: 24px;
  color: #fff;
  background-color: #606266;
  border-color: #fff;
}

.left-btn {
  left: 40px;
  -webkit-transform: translateY(-50%);
  transform: translateY(-50%);
}

.right-btn {
  right: 40px;
  -webkit-transform: translateY(-50%);
  transform: translateY(-50%);
}
</style>